import JSON5 from 'json5'
import { get } from 'lodash-es'
import { cbEnumable, registCbEnumableMockExtend } from './cbEnumable'
import { excuteCode } from './excuteCode'
import { cloneDeepWith } from './jsonUtils'
import Mock from './mockjsOffical/index'
import { getVarContext } from './variableHandlers'

registCbEnumableMockExtend(Mock, cbEnumable)

// _calc表达式
// const balancedParens = /(?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*/
// const calcRegex = new RegExp(`"?{{(${balancedParens.source})}}"?`, 'g')
// const preCalcRegex = new RegExp(`"?{{!(${balancedParens.source})}}"?`, 'g')
const calcRegex = new RegExp('"?{{(.*?)}}"?', 'g')
const preCalcRegex = new RegExp('"?!{{(.*?)}}"?', 'g')

const cannotConvert = (value: string) => value

// 计算字符串中的表达式
const calcStrExpression = (
    str: any,
    config: {
        reg: RegExp
        calcCtx: Record<string, any>
    },
) => {
    // 是否需要换整个值类型
    if (typeof str !== 'string') {
        return str
    }
    let calcedValue = undefined
    let calcedType = undefined
    let isEqual = false
    const result = str.replaceAll(
        config.reg,
        (substring: string, calcExpression) => {
            isEqual = substring === str
            if (typeof calcExpression !== 'string' || !calcExpression) {
                return substring
            }
            let value = substring
            try {
                value = excuteCode(calcExpression, config.calcCtx)
                calcedValue = value
                calcedType = typeof value
            } catch {}

            return value
        },
    )
    if (calcedType === 'string' || !isEqual) return result
    return calcedValue
}

const calcReplace = (
    template: any,
    varContext: Record<string, any>,
    isPre = false,
) => {
    const tType = typeof template
    if ((tType !== 'object' || tType === null) && tType !== 'string') {
        return template
    }
    // 现在只能是字符串或者对象
    // 是否是json对象
    let isJsonStr = false
    let finalTemplate: any = template
    if (tType === 'string') {
        try {
            finalTemplate = JSON5.parse(template)
            isJsonStr = true
        } catch {}
    }

    const reg = isPre ? preCalcRegex : calcRegex
    const calcCtx = {
        ...varContext,
        _t: template,
        _cur: {},
    }
    const calcStrParam = { reg, calcCtx }
    if (typeof finalTemplate === 'string') {
        return calcStrExpression(finalTemplate, calcStrParam)
    } else if (typeof finalTemplate === 'object') {
        let result = cloneDeepWith(finalTemplate, (key, value, keyPath) => {
            const parentKeyPath = keyPath.slice(0, -1)
            const current = get(finalTemplate, parentKeyPath, finalTemplate)
            calcCtx._cur = current
            const newKey = calcStrExpression(key, calcStrParam)
            const newValue = calcStrExpression(value, calcStrParam)
            if (newValue === undefined) {
                return { newKey, newValue, ignore: true }
            }
            return {
                newKey,
                newValue,
            }
        })
        if (isJsonStr) {
            result = JSON.stringify(result, null, 2)
        }
        return result
    }

    return finalTemplate
}

export const mock = (
    template?: string,
    config?: {
        // 当发生错误的时候
        onError?: (error: Error) => void
        // mock之前是否不需要转json
        noJsonParse?: boolean
        // mock之后是否不需要json stringify
        noJsonStringify?: boolean
        // 模板缓存，为了保证像+1这种依赖模板的值，就把模板对象缓存起来
        templateCache?: Record<string, any>
        // 变量上下文
        varContext?: Record<string, any>
    },
) => {
    if (!template) return
    // const { templateCache } = config || {}
    let result = template
    try {
        // 运行代码
        let finalTemplate = calcReplace(
            template,
            config?.varContext || {},
            true,
        )
        // 如果能转json就转josn，转不了就无所谓
        try {
            !config?.noJsonParse && (finalTemplate = JSON5.parse(finalTemplate))
        } catch {}
        // if (template && templateCache && templateCache[template]) {
        //     finalTemplate = templateCache[template]
        // }
        // 生成mock结果
        result = Mock.mock(finalTemplate)
        // if (templateCache) {
        //     templateCache[template] = finalTemplate
        // }
        // 计算表达式
        try {
            result = calcReplace(result, config?.varContext || {})
        } catch {}
        // 如果是json就格式化，格式化不了就无所谓
        try {
            !config?.noJsonStringify &&
                (result = JSON.stringify(result, null, 2))
        } catch {}
    } catch (error) {
        // 生成mock失败了，就通知一下失败的error
        config?.onError?.(error as Error)
    }
    // 返回结果
    return result
}

export const mockToJsonSchema = (template?: string, apiItem?: IApiItem) => {
    if (!template) return {}
    let parsed = template
    try {
        parsed = JSON5.parse(template)
    } catch {}
    const varContext = getVarContext(apiItem?.variables)
    parsed = calcReplace(parsed, varContext || {}, true)
    return Mock.toJSONSchema(parsed)
}
